package com.googlecode.nls4j;

import java.util.EnumSet;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.ResourceBundle.Control;
import java.util.Set;

/**
 * This BundleLoader may be used for loading of string resources and wrapping
 * them into {@linkplain Text} objects. Since this loader is based on
 * {@linkplain EnumBundleLoader}, the supported keys are {@linkplain Enum}s.
 * 
 * @author Andrey Pavlenko
 */
public class EnumTextBundleLoader<K extends Enum<K> & NlsKey<Text>> extends
        EnumBundleLoader<K, Text> {
    /**
     * Same as
     * {@link #EnumTextBundleLoader(String, ClassLoader, ResourceBundle.Control, boolean)
     * EnumTextBundleLoader(null, null, null, false)}.
     */
    public EnumTextBundleLoader() {
        this(null, null, null, false);
    }

    /**
     * Same as
     * {@link #EnumTextBundleLoader(String, ClassLoader, ResourceBundle.Control, boolean)
     * EnumTextBundleLoader(null, null, null, preferDefaultEnumBundle)}.
     */
    public EnumTextBundleLoader(final boolean preferDefaultEnumBundle) {
        this(null, null, null, preferDefaultEnumBundle);
    }

    /**
     * Same as
     * {@link #EnumTextBundleLoader(String, ClassLoader, ResourceBundle.Control, boolean)
     * EnumTextBundleLoader(baseName, null, null, false)}.
     */
    public EnumTextBundleLoader(final String baseName) {
        this(baseName, null, null, false);
    }

    /**
     * Same as
     * {@link #EnumTextBundleLoader(String, ClassLoader, ResourceBundle.Control, boolean)
     * EnumTextBundleLoader(baseName, loader, null, false)}.
     */
    public EnumTextBundleLoader(final String baseName, final ClassLoader loader) {
        this(baseName, loader, null, false);
    }

    /**
     * This constructor calls
     * {@linkplain EnumBundleLoader#EnumBundleLoader(String, ClassLoader, ResourceBundle.Control, boolean)
     * super(baseName, loader, control, preferDefaultEnumBundle)}.
     */
    public EnumTextBundleLoader(final String baseName,
            final ClassLoader loader, final Control control,
            final boolean preferDefaultEnumBundle) {
        super(baseName, loader, control, preferDefaultEnumBundle);
    }

    /**
     * Returns the default EnumTextBundleLoader (i.e. new
     * {@linkplain #EnumTextBundleLoader(String, ClassLoader, ResourceBundle.Control, boolean)
     * EnumTextBundleLoader (null, null, null, preferDefaultEnumBundle)}). This
     * method does not create a new instance on each invocation but returns the
     * cached instance instead.
     */
    @SuppressWarnings("unchecked")
    public static <K extends Enum<K> & NlsKey<Text>> EnumTextBundleLoader<K> getDefault(
            final boolean preferDefaultEnumBundle) {
        return preferDefaultEnumBundle ? InstanceHolder2.instance
                : InstanceHolder1.instance;
    }

    /**
     * Converts the specified ResourceBundle with strings to NlsBundle with
     * {@linkplain Text} objects.
     * 
     * @throws NullPointerException
     *             if one of the arguments is null.
     * @throws ClassCastException
     *             if one ore more resources in the {@code resourceBundle} can
     *             not be casted to {@linkplain String}.
     */
    @Override
    protected NlsBundle<K, Text> toNlsBundle(final Class<K> keyType,
            final ResourceBundle resourceBundle) throws NullPointerException,
            ClassCastException {
        final Set<K> keySet = EnumSet.allOf(keyType);
        final Set<K> unknownKeys = EnumSet.noneOf(keyType);
        final Text[] values = new Text[keySet.size()];
        final Locale locale = resourceBundle.getLocale();
        int i = 0;

        for (final K k : keySet) {
            final String name = k.getName();

            if (resourceBundle.containsKey(name)) {
                values[i++] = new Text(
                        (String) resourceBundle.getObject(name), locale);
            } else {
                unknownKeys.add(k);
                values[i++] = k.getDefaultValue(locale);
            }
        }

        return new EnumBundle<K, Text>(
                locale, keyType, keySet, unknownKeys, values);
    }

    private static final class InstanceHolder1 {
        @SuppressWarnings("rawtypes")
        static final EnumTextBundleLoader instance = new EnumTextBundleLoader(
                null, null, null, false);
    }

    private static final class InstanceHolder2 {
        @SuppressWarnings("rawtypes")
        static final EnumTextBundleLoader instance = new EnumTextBundleLoader(
                null, null, null, true);
    }
}
